/**
 * 
 */
package inference.traffic;

import inference.traffic.TrafficState.LocalState;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.Map.Entry;

import org.apache.commons.math3.random.MersenneTwister;

import cs534.crf.test.TrafficDomain;
import cs534.policy.Function2;

import util.Pair;

/**
 * @author jhostetler
 *
 */
public class LogisticRegression
{
	private static MersenneTwister rng_ = null;
	public static String Rpath = null;
	
	public static ArrayList<Function2<Double, TrafficState, int[]>> features
		= new ArrayList<Function2<Double, TrafficState, int[]>>();
	
	public static void init( final long seed, final String Rpath )
	{
		LogisticRegression.rng_ = new MersenneTwister( seed );
		LogisticRegression.Rpath = Rpath;
	}
	
	public static synchronized NavigableMap<Pair<Integer, Integer>, NavigableMap<Integer, ArrayList<Double>>>
		learnLogisticRegression( 
			final CRFStructure crf_structure,
			NavigableMap< Pair<Integer, Integer>, ArrayList< Pair<LocalState,
			 Pair<Boolean, Boolean> > > > instances ) throws Exception
	{
		NavigableMap< Pair< Integer, Integer >, NavigableMap<Integer, ArrayList<Double>> > ret
			= new TreeMap<Pair<Integer,Integer>, NavigableMap<Integer, ArrayList<Double>> >();
		
		for( Entry<Pair<Integer, Integer>, ArrayList<Pair<LocalState, Pair<Boolean, Boolean>>>>
					entry : instances.entrySet() ){
	
			Pair<Integer, Integer> xion = entry.getKey();
			ArrayList<Pair<LocalState, Pair<Boolean, Boolean>>> inst	= entry.getValue();
		
			NavigableMap<Integer, ArrayList<Double>> coefficients = learnLogisticRegressionInt( crf_structure, xion, inst );
			if( coefficients == null ){
				ret.put( xion, TrafficDomain.null_lr_model.get(xion) );//LOLOLOL
			}else{
				ret.put( xion, coefficients );
			}
		}
	
		return ret;
		
	}
	
	private static synchronized NavigableMap<Integer, ArrayList<Double>> learnLogisticRegressionInt(
			final CRFStructure crf_structure,
			Pair<Integer, Integer> xion,
			ArrayList<Pair<LocalState, Pair<Boolean, Boolean>>> inst) throws InterruptedException, Exception {
	
		//create file
		//call R script
		//read output file
		//parse and return
		
		if( inst.size() == 0 ){
			return null;
		}
		
		String fileName = "multinomial.csv";
		
		PrintStream out = new PrintStream( new File( fileName ) );
		inst.get(0)._o1.printHeader(out);
		for( Pair<LocalState, Pair<Boolean, Boolean>> p : inst ){
			LocalState ls = p._o1;
			Pair<Boolean, Boolean> acts = p._o2;
			ls.printTo(out, crf_structure, xion, ((acts._o1 ? 1 : 0) << 1) | (acts._o2 ? 1 : 0));
		}
		out.close();
		
		NavigableMap<Integer, ArrayList<Double>> params = runR();
	
	    return params;
	    
	}
	
	private static synchronized TreeMap<Integer, ArrayList<Double>> runR() throws IOException, InterruptedException{
		
//		Process p = Runtime.getRuntime().exec("C:\\Program Files\\R\\R-2.14.1\\bin\\Rscript " +
//	    		"logisticRegression.R" );
		Process p = Runtime.getRuntime().exec( Rpath + " multinomialRegression.R" );
	    p.waitFor();
	
	    int exit = p.exitValue();
	    if( exit != 0 ) {
	    	
	    	try{
	    		throw new AssertionError( "exit = " + exit + " (!= 0)" );
	    	}catch( AssertionError e ){
	    		return null;	
	    	}
	    	
	    }
	    
	    //read results
	    Reader rd = new FileReader("multinomial.out");
	    
	    BufferedReader brd = new BufferedReader(rd);
	    
	    String str;
	    
	    TreeMap< Integer, ArrayList<Double> > thisRet = new TreeMap< Integer, ArrayList<Double> >();
	    
	    brd.readLine();
	    while( (str = brd.readLine()) != null ){
	    	String[] splits = str.split(",");
	    	
	    	int action = Integer.parseInt(splits[0].charAt(1) + "" );
	    	ArrayList<Double> inner = new ArrayList<Double>();
	    	
	    	for( int j = 1 ; j < splits.length; ++j ){
	    		String s = splits[j];
	    		double r = 0;
	    		try{
	    			r = Double.parseDouble(s);
	    		}catch( NumberFormatException e ){
	    			r = 0;
	    		}
	    		inner.add( r );
	    	}
	    	
	    	thisRet.put( action, inner );
	    	
	    }
	    
	    brd.close();
	    rd.close();
	    
	    boolean result = new File( "multinomial.csv" ).delete();
	    assert( result );
	    result = new File( "multinomial.out" ).delete();
	    assert( result );
	
	    return thisRet;
	    
	}
	
	public static ArrayList<Double> getLocalActionProbabilities( final LocalState state,
													final CRFStructure crf_structure,
													   final NavigableMap<Integer, ArrayList<Double>> local_lr_model )
	{
		
		ArrayList<Double> ret = new ArrayList<Double>();
		double total = 0.0d;
		
		for( int i = 1; i < 4; ++i ){
			double thisProb = state.multiply(local_lr_model.get(i),  crf_structure, i);
			total += thisProb;
			ret.add( thisProb );
		}
		
		ret.add(0, 1-total);
		
		if( ret.size() != 4 ){
			throw new AssertionError("Not four probabilities");
		}
		
		return ret;
	}
	
	public static NavigableMap<Pair<Integer, Integer>, ArrayList<Double> > getLocalActions(
			NavigableMap<Pair<Integer, Integer>, LocalState> thisInstance,
			NavigableMap<Pair<Integer, Integer>, NavigableMap<Integer, ArrayList<Double>>> lr_model, 
			final CRFStructure crf_structure) {

		TreeMap<Pair<Integer, Integer>, ArrayList<Double> > ret 
			= new TreeMap<Pair<Integer,Integer>, ArrayList<Double> >();
		
		for( Map.Entry<Pair<Integer, Integer>, LocalState> entry : thisInstance.entrySet() ){

			ArrayList<Double> action = new ArrayList<Double>();
			
			if( lr_model != null ){
				NavigableMap<Integer, ArrayList<Double> > params = lr_model.get( entry.getKey() );
				action = getLocalActionProbabilities( entry.getValue(), crf_structure, params );
			}else{
				action = new ArrayList<Double>();
				action.add(0.25d);
				action.add(0.25d);
				action.add(0.25d);
				action.add(0.25d);
//				LocalState ls = entry.getValue();
//				if( ls.light_signal == 1 ){
//					act1 = true;
//					act2 = false;
//				}else{
//					act1 = false;
//					act2 = true;
//				}
				
			}
			
			ret.put( entry.getKey(), action );
		}
		
		return ret;
	}
	
	public static NavigableMap<Pair<Integer, Integer>, Pair<Boolean, Boolean>> sampleLocalActions(
			NavigableMap<Pair<Integer, Integer>, ArrayList<Double> > dist ) {

		TreeMap<Pair<Integer, Integer>, Pair<Boolean, Boolean> > ret 
			= new TreeMap<Pair<Integer,Integer>, Pair< Boolean, Boolean> >();
		
		
		
		for( final Map.Entry<Pair<Integer, Integer>, ArrayList<Double> > e : dist.entrySet() ) {
			int action = rng_.nextInt(dist.size());
			
			boolean act1 = false , act2 = false;
			
			if( action == 2 || action == 3 ){
				act1 = true;
			}else{
				act1 = false;
			}
			
			if( action == 1 || action == 3 ){
				act2 = true;
			}else{
				act2 = false;
			}
			
			ret.put( e.getKey(),  new Pair<Boolean, Boolean>( act1, act2 ) );
		}
		
		return ret;
	}
}
